<?php
declare (strict_types=1);

namespace app\common\auth;

use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\ValidationData;

/**
 * | Notes：JWT验证
 * +----------------------------------------------------------------------
 * | PHP Version 7.2+
 * +----------------------------------------------------------------------
 * | Copyright (c) 2011-2020 https://www.xxq.com.cn, All rights reserved.
 * +----------------------------------------------------------------------
 * | Author: 和好 <389123492@qq.com>
 * +----------------------------------------------------------------------
 * | Date: 2020/4/21 21:12
 * +----------------------------------------------------------------------
 */
class JwtAuth
{
    /**
     * 单例模式 Jwt句柄
     *
     * @var JwtAuth
     */
    private static $instance;

    protected array $config = [
        // KEY(服务端保管)
        'key'         => '3$#%$7887^*&^F687SD!@#(*&^FSD%$$^FDS_+)576%$%F68GDF7SD%687^&687&&',
        // 发行者
        'iss'         => 'https://www.myzy.com.cn',
        // 使用者
        'aud'         => 'https://www.myzy.com.cn',
        // Jwt身份ID
        'id'          => 'MINGYANGB7D0C9D279892857DE5490AB856772FE',
        // 过期时间
        'expire_time' => 604800,
    ];

    /**
     * Jwt token
     *
     * @var
     */
    private $token;

    /**
     * Jwt parser_token
     *
     * @var
     */
    private $parser_token;

    /**
     * Jwt expire_time
     *
     * @var
     */
    private $expire_time;

    /**
     * claim uuid
     *
     * @var int
     */
    private int $uuid;

    /**
     * claim uuid
     *
     * @var int
     */
    private int $client_type;

    private function __construct()
    {
        // 判断是否有设置配置项.此配置项为数组，做一个兼容
        if (config('jwt')) {
            // 合并,覆盖配置
            $this->config = array_merge($this->config, config('jwt'));

            if (!empty(config('system.expire_time'))) {
                $this->config['expire_time'] = config('system.expire_time');
            }
        }
    }

    /**
     * 单例句柄
     *
     * @return JwtAuth
     */
    public static function getInstance(): JwtAuth
    {
        if (is_null(self::$instance)) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * 获取配置
     *
     * @return array
     */
    public function getConfig(): array
    {
        return $this->config;
    }

    /**
     * 获取用户ID
     *
     * @return int
     */
    public function getUuid(): int
    {
        return (int)$this->uuid;
    }

    /**
     * 获取用户客户端类型
     *
     * @return int
     */
    public function getClientType(): int
    {
        return (int)$this->client_type;
    }

    /**
     * 设置用户ID
     *
     * @param $uuid
     *
     * @return $this
     */
    public function setUuid($uuid): JwtAuth
    {
        $this->uuid = $uuid;
        return $this;
    }

    /**
     * 获取过期时间
     *
     * @return mixed
     */
    public function getExpireTime()
    {
        return $this->expire_time;
    }

    /**
     * 获取token
     *
     * @return string
     */
    public function getToken(): string
    {
        return (string)$this->token;
    }

    /**
     * 设置token
     *
     * @param $token
     *
     * @return JwtAuth
     */
    public function setToken($token): JwtAuth
    {
        $this->token = $token;

        return $this;
    }

    /**
     * 创建Token
     *
     * @param $uuid
     * @param $client_type
     *
     * @return $this
     */
    public function makeToken($uuid, $client_type): JwtAuth
    {
        $time              = time();
        $this->expire_time = $time + $this->config['expire_time'];
        $signer            = new Sha256();
        $this->token       = (new Builder())
            ->issuedBy($this->config['iss']) // 发行
            ->permittedFor($this->config['aud']) // 听众
            ->identifiedBy($this->config['id'], true) // id
            ->issuedAt($time) // 发行时间
            ->canOnlyBeUsedAfter($time + 0) // 距离解冻生效时间 例如：setCurrentTime60秒生效 在20秒之前则是无效的，20秒后则有效
            ->expiresAt($this->expire_time)     // 生效以后过期时长
            ->withClaim('uuid', $uuid)                        // 配置一个新的字段
            ->withClaim('client_type', $client_type)          // 配置一个新的字段
            ->getToken($signer, new Key($this->config['key'])); // 创建token

        return $this;
    }

    public function parserToken(): JwtAuth
    {
        $this->parser_token = (new Parser())->parse($this->token);
        $this->uuid         = (int)$this->parser_token->getClaim('uuid');
        $this->client_type  = (int)$this->parser_token->getClaim('client_type');

        return $this;
    }

    public function verify()
    {
        return $this->parser_token->verify(new Sha256(), $this->config['key']);
    }

    public function validate()
    {
        $data = new ValidationData();
        $data->setId($this->config['id']);
        $data->setIssuer($this->config['iss']);
        $data->setAudience($this->config['aud']);
        $data->setCurrentTime(time() + 0); // 生效时间   60秒生效时间

        return $this->parser_token->validate($data);
    }

    private function __clone()
    {

    }
}
